export namespace Swagger {
    export type DataType = 'integer' | 'number' | 'boolean' | 'string' | 'array' | 'object' | 'file'

    export type DataFormat =
        | 'int32'
        | 'int64'
        | 'float'
        | 'double'
        | 'byte'
        | 'binary'
        | 'date'
        | 'date-time'
        | 'password'

    export type Protocol = 'http' | 'https' | 'ws' | 'wss'

    export type SupportedSpecMajorVersion = 2 | 3

    export interface Spec {
        info: Info
        tags?: Tag[]
        externalDocs?: ExternalDocs
    }

    export interface Spec2 extends Spec {
        swagger: '2.0'
        host?: string
        basePath?: string
        schemes?: Protocol[]
        consumes?: string[]
        produces?: string[]
        paths: { [name: string]: Path }
        definitions?: { [name: string]: Schema }
        parameters?: { [name: string]: Parameter }
        responses?: { [name: string]: Response }
        security?: Security[]
        securityDefinitions?: { [name: string]: Security }
    }

    export interface Spec3 extends Spec {
        openapi: '3.0.0'
        servers: Server[]
        components: Components
        paths: { [name: string]: Path3 }
    }

    export interface Components {
        callbacks?: { [name: string]: any }
        examples?: { [name: string]: Example3 | string }
        headers?: { [name: string]: any }
        links?: { [name: string]: any }
        parameters?: { [name: string]: Parameter }
        requestBodies?: { [name: string]: any }
        responses?: { [name: string]: Response }
        schemas?: { [name: string]: Schema | Schema3 }
        securitySchemes?: { [name: string]: Security }
    }

    export interface Server {
        url: string
        description?: string
        variables?: any
    }

    export interface Info {
        title: string
        version?: string
        description?: string
        termsOfService?: string
        contact?: Contact
        license?: License
    }

    export interface Contact {
        name?: string
        email?: string
        url?: string
    }

    export interface License {
        name: string
        url?: string
    }

    export interface ExternalDocs {
        url: string
        description?: string
    }

    export interface Tag {
        name: string
        description?: string
        externalDocs?: ExternalDocs
    }

    export interface Example {
        examples?: { [exampleName: string]: Swagger.Example3 }
    }

    export interface Example3 {
        value: unknown
        summary?: string
        description?: string
    }

    export interface BaseParameter extends BaseSchema {
        name: string
        in: 'query' | 'header' | 'path' | 'formData' | 'body'
        required?: boolean
        description?: string
        example?: unknown
        examples?: { [name: string]: Example3 | string }
        schema: Schema
        type: DataType
        format?: DataFormat
    }

    export interface BodyParameter extends BaseParameter {
        in: 'body'
    }

    export interface QueryParameter extends BaseParameter {
        in: 'query'
        allowEmptyValue?: boolean
        collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes' | 'multi'
    }

    export interface PathParameter extends BaseParameter {
        in: 'path'
    }

    export interface HeaderParameter extends BaseParameter {
        in: 'header'
    }

    export interface FormDataParameter extends BaseParameter {
        in: 'formData'
        collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes' | 'multi'
    }

    export type Parameter =
        | BodyParameter
        | FormDataParameter
        | QueryParameter
        | PathParameter
        | HeaderParameter

    export interface Path {
        $ref?: string
        get?: Operation
        put?: Operation
        post?: Operation
        delete?: Operation
        options?: Operation
        head?: Operation
        patch?: Operation
        parameters?: Parameter[]
    }

    export interface Path3 {
        $ref?: string
        get?: Operation3
        put?: Operation3
        post?: Operation3
        delete?: Operation3
        options?: Operation3
        head?: Operation3
        patch?: Operation3
        parameters?: Parameter[]
    }

    export interface Operation {
        tags?: string[]
        summary?: string
        description?: string
        externalDocs?: ExternalDocs
        operationId: string
        consumes?: string[]
        produces?: string[]
        parameters?: Parameter[]
        responses: { [name: string]: Response }
        schemes?: Protocol[]
        deprecated?: boolean
        security?: Security[]
        // Used to apply extensions to paths
        [key: string]: unknown
    }

    export interface Operation3 {
        tags?: string[]
        summary?: string
        description?: string
        externalDocs?: ExternalDocs
        operationId: string
        consumes?: string[]
        parameters?: Parameter[]
        responses: { [name: string]: Response3 }
        schemes?: Protocol[]
        deprecated?: boolean
        security?: Security[]
        requestBody?: RequestBody
    }

    export interface RequestBody {
        content: { [name: string]: MediaType }
        description?: string
        required?: boolean
    }

    export interface MediaType {
        schema?: Schema3
        example?: unknown
        examples?: { [name: string]: Example3 | string }
        encoding?: { [name: string]: any }
    }

    export interface Response {
        description: string
        schema?: Schema
        headers?: { [name: string]: Header }
        examples?: Example
    }

    export interface Response3 {
        description: string
        content?: { [name: string]: Schema & Example }
        headers?: { [name: string]: Header3 }
    }

    export interface BaseSchema {
        type?: string
        format?: DataFormat
        $ref?: string
        title?: string
        description?: string
        default?: string | boolean | number | any
        multipleOf?: number
        maximum?: number
        exclusiveMaximum?: number
        minimum?: number
        exclusiveMinimum?: number
        maxLength?: number
        minLength?: number
        pattern?: string
        maxItems?: number
        minItems?: number
        uniqueItems?: boolean
        maxProperties?: number
        minProperties?: number
        enum?: Array<string | number | null>
        'x-enum-varnames'?: string[]
        items?: BaseSchema
    }

    export interface Schema3 extends Omit<Schema, 'type'> {
        type?: DataType
        nullable?: boolean
        anyOf?: BaseSchema[]
        allOf?: BaseSchema[]
    }

    export interface Schema2 extends Schema {
        ['x-nullable']?: boolean
    }

    export interface Schema extends BaseSchema {
        type?: DataType
        format?: DataFormat
        additionalProperties?: boolean | BaseSchema
        properties?: { [propertyName: string]: Schema3 }
        discriminator?: string
        readOnly?: boolean
        xml?: XML
        externalDocs?: ExternalDocs
        example?: unknown
        required?: string[]
    }

    export interface Header {
        description?: string
        type: 'string' | 'number' | 'integer' | 'boolean' | 'array'
        format?: string
        items?: BaseSchema
        collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes' | 'multi'
        default?: string | boolean | number | any
        maximum?: number
        exclusiveMaximum?: boolean
        minimum?: number
        exclusiveMinimum?: boolean
        maxLength?: number
        minLength?: number
        pattern?: string
        maxItems?: number
        minItems?: number
        uniqueItems?: boolean
        enum?: Array<string | number | null>
        multipleOf?: number
    }

    export interface Header3 extends BaseSchema {
        required?: boolean
        description?: string
        example?: unknown
        examples?: {
            [name: string]: Example3 | string
        }
        schema: Schema
        type?: DataType
        format?: DataFormat
    }

    export interface XML {
        type?: string
        namespace?: string
        prefix?: string
        attribute?: string
        wrapped?: boolean
    }

    interface BaseSecurity {
        description?: string
    }

    interface BaseOAuthSecurity extends BaseSecurity {
        scopes?: OAuthScope
    }

    export interface BasicSecurity3 extends BaseSecurity {
        type: 'http'
        scheme: 'basic'
    }

    export interface BasicSecurity extends BaseSecurity {
        type: 'basic'
    }

    export interface ApiKeySecurity extends BaseSecurity {
        type: 'apiKey'
        name: string
        in: 'query' | 'header'
    }

    export interface OAuth2Security3 extends BaseSecurity {
        type: 'oauth2'
        flows: OAuthFlow
    }

    export interface OAuth2SecurityFlow3 extends BaseSecurity {
        tokenUrl?: string
        authorizationUrl?: string
        scopes?: OAuthScope
    }

    export interface OAuth2ImplicitSecurity extends BaseOAuthSecurity {
        type: 'oauth2'
        description?: string
        flow: 'implicit'
        authorizationUrl: string
    }

    export interface OAuth2PasswordSecurity extends BaseOAuthSecurity {
        type: 'oauth2'
        flow: 'password'
        tokenUrl: string
    }

    export interface OAuth2ApplicationSecurity extends BaseOAuthSecurity {
        type: 'oauth2'
        flow: 'application'
        tokenUrl: string
    }

    export interface OAuth2AccessCodeSecurity extends BaseOAuthSecurity {
        type: 'oauth2'
        flow: 'accessCode'
        tokenUrl: string
        authorizationUrl: string
    }

    export interface OAuthScope {
        [scopeName: string]: string
    }

    export type OAuthFlow = {
        [flowName in OAuth2FlowTypes]?: OAuth2SecurityFlow3
    }
    export type OAuth2FlowTypes =
        | 'authorizationCode'
        | 'implicit'
        | 'password'
        | 'clientCredentials'
    export type Security =
        | BasicSecurity
        | BasicSecurity3
        | ApiKeySecurity
        | OAuth2AccessCodeSecurity
        | OAuth2ApplicationSecurity
        | OAuth2ImplicitSecurity
        | OAuth2PasswordSecurity
        | OAuth2Security3
}
